package fr.cephb.operonj.tools.cmdline;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;


public class AbstractApplication
	{
	public static final Logger LOG= Logger.getLogger("fr.cephb.operonj");
	private Environment  env=null;
	protected EnvironmentConfig envCfg=new EnvironmentConfig();
	private Schema schema=null;
	private File dbHome=null;
	private Properties preferences=null;
	
	protected AbstractApplication()
		{
		LOG.setLevel(Level.OFF);
		envCfg.setAllowCreate(false);
		envCfg.setReadOnly(true);
		}
	
	public Environment getEnvironment()
		{
		return env;
		}
	
	public String getName()
		{
		return getClass().getSimpleName();
		}
	
	public String getDescription()
		{
		return getName();
		}
	
	public String getAuthor()
		{
		return "Pierre Lindenbaum";
		}
	
	public String getAffliation()
		{
		return "CEPH";
		}
	
	private static Properties loadPrefs(InputStream in)
		{
		Properties p=new Properties();
		if(in!=null)
			{
			try
				{
				p.loadFromXML(in);
				}
			catch (IOException e)
				{
				LOG.warning(e.getMessage());
				}
			finally
				{
				try { in.close(); } catch(IOException e) {}
				}
			}
		return p;
		}
	
	public Properties getPreferences()
		{
		if(this.preferences!=null) return preferences;
		final String path="/META-INF/operonj.properties";
		this.preferences=loadPrefs(getClass().getResourceAsStream(path));

		
		return this.preferences;
		}
	
	protected InputStream openStream(String name) throws IOException
		{
		InputStream in=null;
		if( name.startsWith("http://") ||
			name.startsWith("https://") ||
			name.startsWith("ftp://")
			)
			{
			LOG.info("open URL");
			in=new URL(name).openStream();
			}
		else
			{
			LOG.info("open file");
			in= new FileInputStream(name);
			}
		if(name.endsWith(".gz"))
			{
			LOG.info("input is g-zipped");
			in= new GZIPInputStream(in);
			}
		return in;
		}
	
	
	
	protected Schema getSchema() throws SAXException,IOException
		{
		if(schema!=null) return schema;
		SchemaFactory factory=SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		LOG.info("reading schema");
		InputStream schemaIn= getClass().getResourceAsStream("/META-INF/schema/operonj.xsd");
		this.schema= factory.newSchema(new StreamSource(schemaIn));
		schemaIn.close();
		LOG.info("read schema : ok");
		return this.schema;
		}
	
	public void open() throws DatabaseException,IOException
		{
		close();
		if(dbHome==null)
			{
			dbHome=getDefaultHome();
			if(dbHome==null) throw new FileNotFoundException("DB_HOME undefined");
			}
		if(!dbHome.exists())
			{
			if(!this.envCfg.getAllowCreate()) throw new FileNotFoundException("Unknown:"+dbHome);
			if(!dbHome.mkdir()) throw new IOException("Cannot create :"+dbHome);
			}
		if(!dbHome.isDirectory()) throw new IllegalArgumentException("Not a directory:"+dbHome);
		
		this.env= new Environment(this.dbHome, this.envCfg);
		}
	
	public File getDefaultHome()
		{
		try
			{
			String dirHome= System.getProperty("DB_HOME");
			if(dirHome!=null)
				{
				LOG.info("DB_HOME defined in System.properties:"+dirHome);
				return new File(dirHome);
				}
			final String metaInf="/META-INF/operonj.properties";
			InputStream in= getClass().getResourceAsStream(metaInf);
			if(in==null)
				{
				LOG.info("cannot get resource :"+metaInf);
				}
			else
				{
				Properties prop= new Properties();
				prop.loadFromXML(in);
				in.close();
				dirHome= prop.getProperty("DB_HOME");
				if(dirHome!=null)
					{
					LOG.info("DB_HOME defined in "+ metaInf +":"+dirHome);
					return new File(dirHome);
					}
				}
			}
		catch (Exception e)
			{
			LOG.severe("Error:"+e.getMessage());
			}
		LOG.warning("Error: Cannot find default DB_HOME");
		return null;
		}
	
	public void close()
		{
		if(this.env!=null )
			{
			try { 
				if(!this.env.getConfig().getReadOnly())
					{
					this.env.cleanLog();
					}
				}
			catch(DatabaseException err)
				{ }
			try { this.env.close(); }
			catch(DatabaseException err)
				{ err.printStackTrace();}
			}
		
		env=null;
		}
	
	protected void usage(PrintStream out)
		{
		out.println("General Options:");
		out.println(" -help (this screen)");
		out.println(" -db-home <DB_HOME> (optional)");
		out.println(" -log-level <level> default:"+LOG.getLevel());
		}
	
	protected int processArg(String args[],int optind)
		{
		if(args[optind].equals("-help"))
			{
			System.out.println(getName());
			System.out.println(getAuthor()+" : "+getAffliation());
			System.out.println(getDescription());
			usage(System.out);
			System.exit(0);
			}
		else if(args[optind].equals("-log-level"))
			{
			Level L=Level.parse(args[++optind]);
			if(L==null) throw new IllegalArgumentException("Bad level:"+L);
			LOG.setLevel(L);
			return optind;
			}
		else if(args[optind].equals("-db-home"))
			{
			this.dbHome=new File(args[++optind]);
			return optind;
			}
		return -1;
		}
	
	protected int processArgs(String args[])
		{
		int optind=0;
		while(optind< args.length)
			{
			int n= processArg(args, optind);
			if(n!=-1)
				{
				optind=n;
				}
			else if(args[optind].equals("--"))
				{
				optind++;
				break;
				}
			else if(args[optind].startsWith("-"))
				{
				System.err.println("Unknown option "+args[optind]);
				System.err.println("type -help for more information");
				System.exit(-1);
				}
			else 
				{
				break;
				}
			++optind;
			}
		return optind;
		}
	}
